package com.semlab.client.fwk.ap;

import java.util.Set;
import com.google.gwt.place.shared.PlaceHistoryMapper;

import static com.semlab.client.fwk.ap.PlaceConstants.*;

/**
 * PlaceHistoryMapper interface is used to attach all places which the
 * PlaceHistoryHandler should be aware of. This is done via the @WithTokenizers
 * annotation or by extending PlaceHistoryMapperWithFactory and creating a
 * separate TokenizerFactory.
 */
// @WithTokenizers( { Place.Tokenizer.class })
public class AppPlaceHistoryMapper implements PlaceHistoryMapper {
	
	private static String escape(String value) {
		return value.replaceAll(PARAM_SEPARATOR, PARAM_ESCAPE).replaceAll(VALUE_SEPARATOR, VALUE_ESCAPE);
	}

	private static String unescape(String value) {
		return value.replaceAll(PARAM_ESCAPE, PARAM_SEPARATOR).replaceAll(VALUE_ESCAPE, VALUE_SEPARATOR);
	}

	@Override
	public Place getPlace(String token) {
		Place place = null;
		int split = token.indexOf(PARAM_SEPARATOR);
		if (split == 0) {
			throw new IllegalArgumentException("Place id is missing.");
		} else if (split == -1) {
			int idsplit = token.indexOf(ID_SEPARATOR);
			if (idsplit == -1) {
				throw new IllegalArgumentException("Place id is in bad format.");
			} else {
				place = new Place(token.substring(0, idsplit), token.substring(idsplit + 1));
			}
		} else if (split >= 0) {
			String idToken = token.substring(0, split);
			int idsplit = idToken.indexOf(ID_SEPARATOR);
			if (idsplit == -1) {
				throw new IllegalArgumentException("Place id is in bad format.");
			} else {
				place = new Place(idToken.substring(0, idsplit), idToken.substring(idsplit + 1));
			}
			String paramsChunk = token.substring(split + 1);
			String[] paramTokens = paramsChunk.split(PARAM_PATTERN);
			for (String paramToken : paramTokens) {
				String[] param = paramToken.split(VALUE_PATTERN);
				if (param.length == 1){
					place = place.with(unescape(param[0]), "");
				}else if (param.length == 2){
					String key = unescape(param[0]);
					String value = unescape(param[1]);
					if(value.contains(ID_SEPARATOR)){
						String[] pars = value.split(ID_SEPARATOR);
						Place withPlace = new Place(pars[0], pars[1]);
						place = place.with(new Place.Parameter<Place>(key, Place.Place), withPlace);
					}else{
						place = place.with(key, value);
					}
				}else
					throw new IllegalArgumentException("Bad parameter: Parameters require a single '" + VALUE_SEPARATOR + "' between the key and value.");

			}
		}
		return place;
	}

	@Override
	public String getToken(com.google.gwt.place.shared.Place place) {
		Place myPlace = (Place) place;
		StringBuilder out = new StringBuilder();
		out.append(myPlace.getContainerId());
		out.append(ID_SEPARATOR);
		out.append(myPlace.getActivityId());

		Set<String> params = myPlace.getParameterNames();
		if (params != null && params.size() > 0) {
			for (String name : params) {
				out.append(PARAM_SEPARATOR);
				out.append(escape(name)).append(VALUE_SEPARATOR).append(escape(myPlace.get(name)));
			}
		}
		return out.toString();	
	}

}
